package com.cctc.juc.demo.Bitc.Ammap.GThreadLocal.AThreadLocal;

import lombok.Data;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class BThreadLocalExactDemo01 {
    @Data
    private static class Foo {
        private static final AtomicInteger ID = new AtomicInteger(0);
        private int index = 0;
        private int data = 10;

        public Foo() {
            index = ID.getAndIncrement();
        }

        @Override
        public String toString() {
            return index + "@Foo{data=" + data + "}";
        }
    }

    private static final ThreadLocal<Foo> THREAD_LOCAL = ThreadLocal.withInitial(() -> new Foo());

    public static void main(String[] args) {
        ExecutorService executorService = new ThreadPoolExecutor(
                1, 1,
                60, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(),
                Thread::new,
                new ThreadPoolExecutor.DiscardPolicy()
        ) {
            @Override
            protected void beforeExecute(Thread t, Runnable r) {
                THREAD_LOCAL.remove();
            }

            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                THREAD_LOCAL.remove();
            }
        };
        for (int i = 1; i <= 5; i++) {
            final int index = i;
            executorService.execute(() -> {
                if (index == 1) {
                    System.out.printf("线程池【派生】线程名称：%s，线程本地变量为：%s%n",
                            Thread.currentThread().getName(),
                            THREAD_LOCAL.get());
                } else {
                    System.out.printf("线程池【复用】线程名称：%s，线程本地变量为：%s -> %s%n",
                            Thread.currentThread().getName(),
                            THREAD_LOCAL.get(),
                            "ThreadLocal 线程池问题解决办法：调用线程池的 beforeExecute() 或 afterExecute() 方法主动清除【复用】线程的 threadLocalMap 缓存。");
                }

                Foo foo = THREAD_LOCAL.get();
                foo.setData(foo.getData() + index);
                THREAD_LOCAL.set(foo);

                if (index == 1) {
                    System.out.printf("线程池【派生】线程名称：%s，更新后的线程本地变量为：%s%n",
                            Thread.currentThread().getName(),
                            THREAD_LOCAL.get());
                } else {
                    System.out.printf("线程池【复用】线程名称：%s，更新后的线程本地变量为：%s%n",
                            Thread.currentThread().getName(),
                            THREAD_LOCAL.get());
                }

                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }

        // 休眠一会，保证下面的代码执行一定在上面代码之后执行。
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        executorService.shutdown();

    }


}
